import Data.Char (isDigit)

data Token = LeftBracket 
           | RightBracket
           | Plus
           | Star
           | Number Integer 
           | EOF
             deriving (Show, Eq)

readToken :: String -> (String, Token)
readToken input 
    | null input' = ("", EOF)
    | otherwise = case c of
        '(' -> (cs, LeftBracket)
        ')' -> (cs, RightBracket)
        '+' -> (cs, Plus)
        '*' -> (cs, Star)
        _ -> if isDigit c 
            then readNumber input'
            else error "Unkonw character"
    where
        input' = dropWhile (==' ') input
        c = head input'
        cs = tail input'
        
readNumber :: String -> (String, Token)
readNumber input = (rest, (Number $ read numberString))
    where
        numberString = takeWhile isDigit input
        rest = drop (length numberString) input

lexing :: String -> [Token]
lexing str 
    | token == EOF = [token]
    | otherwise    = token : (lexing str')
    where
        (str', token) = readToken str

data Oper = Add | Mul deriving (Show, Eq)

data Expr = LiteralExpr Integer
          | BinaryExpr Expr Oper Expr
          | GroupExpr Expr
            deriving (Show)

evaluate :: Expr -> Integer
evaluate expr = case expr of
    LiteralExpr value -> value
    BinaryExpr left oper right -> case oper of
        Add -> (evaluate left) + (evaluate right)
        Mul -> (evaluate left) * (evaluate right)
    GroupExpr groupExpr -> evaluate groupExpr 

parseLiteral :: [Token] -> ([Token], Expr)
parseLiteral ((Number value):rest) = (rest, LiteralExpr value)

isOper :: Token -> Bool
isOper Star = True
isOper Plus = True
isOper _ = False

parseOper :: Token -> Oper
parseOper Plus = Add
parseOper Star = Mul

parseUnary :: [Token] -> ([Token], Expr)
parseUnary tokens@(token:rest) = case token of
    LeftBracket -> parseGroup tokens
    RightBracket -> error "No left bracket match"
    Plus -> error "No left number"
    Star -> error "No left number"
    Number _ -> parseLiteral tokens
    EOF -> error "End of file"

parseBinary :: [Token] -> ([Token], Expr)
parseBinary tokens = doParseBinary tokens' left
    where
        (tokens', left)  = parseUnary tokens 

        doParseBinary :: [Token] -> Expr -> ([Token], Expr)
        doParseBinary tokens left = 
            if isOper operToken 
                then doParseBinary tokens' binExpr
                else (tokens, left)
            where
                operToken = head tokens
                oper = parseOper operToken
                (tokens', right) = parseUnary $ tail tokens
                binExpr = BinaryExpr left oper right

parseGroup :: [Token] -> ([Token], Expr)
parseGroup tokens = (tail tokens', GroupExpr expr)
    where
        -- leftBracket = head . tokens
        (tokens', expr) = parseBinary $ tail tokens
        -- rightBracket = head . tokens'

parse :: [Token] -> Expr
parse [] = error "No token"
parse tokens = expr
    where
        (_, expr) = parseBinary tokens

calculate :: String -> Integer
calculate = evaluate . parse . lexing

--------------------------------------------------------

main = do
    input <- readFile "input.txt"

    print . sum . map calculate . lines $ input 